'use strict';

var $ = require('jquery');
var isMobile = require('./is-mobile');
var detectCompact = require('./detect-compact');

module.exports = (function() {
  /* ===========================================================
   * bootstrap-tooltip.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#tooltips
   * Inspired by the original jQuery.tipsy by Jason Frame
   * ===========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ========================================================== */

  /*

  HEY TROUPER! This is an exact copy of the original Bootstrap tooltip but
  is disabled by default if you're on a mobile device.

*/

  !(function($) {
    /* TOOLTIP PUBLIC CLASS DEFINITION
     * =============================== */

    var Tooltip = function(element, options) {
      this.init('tooltip', element, options);
    };

    Tooltip.prototype = {
      constructor: Tooltip,

      init: function(type, element, options) {
        var eventIn, eventOut, triggers, trigger, i;

        this.type = type;
        this.$element = $(element);
        this.options = this.getOptions(options);
        this.enabled = !isMobile();

        triggers = this.options.trigger.split(' ');

        for (i = triggers.length; i--; ) {
          trigger = triggers[i];
          if (trigger == 'click') {
            this.$element.on(
              'click.' + this.type,
              this.options.selector,
              $.proxy(this.toggle, this)
            );
          } else if (trigger != 'manual') {
            eventIn = trigger == 'hover' ? 'mouseenter' : 'focus';
            eventOut = trigger == 'hover' ? 'mouseleave' : 'blur';
            this.$element.on(
              eventIn + '.' + this.type,
              this.options.selector,
              $.proxy(this.onenter, this)
            );
            this.$element.on(
              eventOut + '.' + this.type,
              this.options.selector,
              $.proxy(this.onleave, this)
            );
          }
        }

        this.options.selector
          ? (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' }))
          : this.fixTitle();
      },

      getOptions: function(options) {
        options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options);

        if (options.delay && typeof options.delay === 'number') {
          options.delay = {
            show: options.delay,
            hide: options.delay
          };
        }

        return options;
      },

      onenter: function(e) {
        this.enter(e.currentTarget);
      },

      enter: function(currentTarget) {
        currentTarget = currentTarget || this.$element[0];

        var defaults = $.fn[this.type].defaults,
          options = {},
          self;

        this._options &&
          $.each(
            this._options,
            function(key, value) {
              if (defaults[key] != value) options[key] = value;
            },
            this
          );

        self = $(currentTarget)
          [this.type](options)
          .data(this.type);

        self.hoverState = 'in';

        if (!self.options.delay || !self.options.delay.show) return self.show();

        clearTimeout(this.timeout);
        this.timeout = setTimeout(function() {
          if (self.hoverState == 'in') self.show();
        }, self.options.delay.show);
      },

      onleave: function(e) {
        this.leave(e.currentTarget);
      },

      leave: function(currentTarget) {
        currentTarget = currentTarget || this.$element[0];
        var self = $(currentTarget)
          [this.type](this._options)
          .data(this.type);

        if (self.hoverState !== 'in') return;
        self.hoverState = 'out';

        if (this.timeout) clearTimeout(this.timeout);
        if (!self.options.delay || !self.options.delay.hide) return self.hide();

        this.timeout = setTimeout(function() {
          if (self.hoverState == 'out') self.hide();
        }, self.options.delay.hide);
      },

      // eslint-disable-next-line complexity
      show: function() {
        var $tip,
          pos,
          actualWidth,
          actualHeight,
          placement,
          tp,
          e = $.Event('show');

        if (Tooltip.prev && Tooltip.prev !== this) Tooltip.prev.hide();
        Tooltip.prev = this;

        if (this.hasContent() && this.enabled) {
          this.$element.trigger(e);
          if (e.isDefaultPrevented()) return;
          $tip = this.tip();
          this.setContent();

          if (this.options.animation) {
            $tip.addClass('fade');
          }

          placement =
            typeof this.options.placement === 'function'
              ? this.options.placement.call(this, $tip[0], this.$element[0])
              : this.options.placement;

          $tip.detach().css({ top: 0, left: 0, display: 'block' });

          this.options.container
            ? $tip.appendTo(this.options.container)
            : $tip.insertAfter(this.$element);

          pos = this.getPosition();

          actualWidth = $tip[0].offsetWidth;
          actualHeight = $tip[0].offsetHeight;

          placement = this.options.placement;
          if (placement === 'vertical')
            placement = this.selectBestVerticalPlacement($tip, this.$element);
        }

        switch (placement) {
          case 'bottom':
            tp = { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 };
            break;
          case 'top':
            tp = { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 };
            break;
          case 'left':
            tp = { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth };
            break;
          case 'right':
            tp = { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width };
            break;
        }

        this.applyPlacement(tp, placement);
        this.$element.trigger('shown');
      },

      selectBestVerticalPlacement: function(div, target) {
        var rect = target[0].getBoundingClientRect();
        if (rect.top < 60) {
          return 'bottom';
        } else {
          return 'top';
        }
        // var panel = target.offsetParent();
        // if(!panel) return 'bottom';
        //
        // if(target.offset().top + div.height() + 20 >= panel[0].clientHeight) {
        //   return 'top';
        // }
        //
        // return 'bottom';
      },

      applyPlacement: function(offset, placement) {
        var $tip = this.tip(),
          width = $tip[0].offsetWidth,
          height = $tip[0].offsetHeight,
          actualWidth,
          actualHeight,
          delta,
          replace;

        $tip
          .offset(offset)
          .addClass(placement)
          .addClass('in');

        actualWidth = $tip[0].offsetWidth;
        actualHeight = $tip[0].offsetHeight;

        if (placement == 'top' && actualHeight != height) {
          offset.top = offset.top + height - actualHeight;
          replace = true;
        }

        if (placement == 'bottom' || placement == 'top') {
          delta = 0;

          if (offset.left < 0) {
            delta = offset.left * -2;
            offset.left = 0;
            $tip.offset(offset);
            actualWidth = $tip[0].offsetWidth;
            actualHeight = $tip[0].offsetHeight;
          }

          this.replaceArrow(delta - width + actualWidth, actualWidth, 'left');
        } else {
          this.replaceArrow(actualHeight - height, actualHeight, 'top');
        }

        if (replace) $tip.offset(offset);
      },

      replaceArrow: function(delta, dimension, position) {
        this.arrow().css(position, delta ? 50 * (1 - delta / dimension) + '%' : '');
      },

      setContent: function() {
        var $tip = this.tip(),
          title = this.getTitle();

        $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title);
        $tip.removeClass('fade in top bottom left right');
      },

      hide: function() {
        var $tip = this.tip();
        var e = $.Event('hide');

        this.$element.trigger(e);
        if (e.isDefaultPrevented()) return;

        $tip.removeClass('in');

        function removeWithAnimation() {
          var timeout = setTimeout(function() {
            $tip.off($.support.transition.end).detach();
          }, 500);

          $tip.one($.support.transition.end, function() {
            clearTimeout(timeout);
            $tip.detach();
          });
        }

        $.support.transition && this.$tip.hasClass('fade') ? removeWithAnimation() : $tip.detach();

        this.$element.trigger('hidden');

        return this;
      },

      fixTitle: function() {
        var $e = this.$element;
        if ($e.attr('title') && typeof $e.attr('data-original-title') !== 'string') {
          $e.attr('data-original-title', $e.attr('title') || '').attr('title', '');
        }
      },

      hasContent: function() {
        return this.getTitle();
      },

      getPosition: function() {
        var el = this.$element[0];
        return $.extend(
          {},
          typeof el.getBoundingClientRect === 'function'
            ? el.getBoundingClientRect()
            : {
                width: el.offsetWidth,
                height: el.offsetHeight
              },
          this.$element.offset()
        );
      },

      getTitle: function() {
        var title,
          $e = this.$element,
          o = this.options;

        title = typeof o.title === 'function' ? o.title.call($e[0]) : o.title;

        return title;
      },

      tip: function() {
        return (this.$tip = this.$tip || $(this.options.template));
      },

      arrow: function() {
        return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'));
      },

      validate: function() {
        if (!this.$element[0].parentNode) {
          this.hide();
          this.$element = null;
          this.options = null;
        }
      },

      enable: function() {
        this.enabled = true;
      },

      disable: function() {
        this.enabled = false;
      },

      toggleEnabled: function() {
        this.enabled = !this.enabled;
      },

      toggle: function(e) {
        var self = e
          ? $(e.currentTarget)
              [this.type](this._options)
              .data(this.type)
          : this;
        self.tip().hasClass('in') ? self.hide() : self.show();
      },

      destroy: function() {
        this.hide()
          .$element.off('.' + this.type)
          .removeData(this.type);
      }
    };

    /* TOOLTIP PLUGIN DEFINITION
     * ========================= */

    var old = $.fn.tooltip;

    var compact = detectCompact();

    $.fn.tooltip = function(option) {
      if (compact) return; // Do nothing if compact

      return this.each(function() {
        var $this = $(this),
          data = $this.data('tooltip'),
          options = typeof option === 'object' && option;
        if (!data) $this.data('tooltip', (data = new Tooltip(this, options)));
        if (typeof option === 'string') data[option]();
      });
    };

    $.fn.tooltip.Constructor = Tooltip;

    $.fn.tooltip.defaults = {
      animation: true,
      placement: 'top',
      selector: false,
      template:
        '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
      trigger: 'hover focus',
      title: '',
      delay: 0,
      html: false,
      container: false
    };

    /* TOOLTIP NO CONFLICT
     * =================== */

    $.fn.tooltip.noConflict = function() {
      $.fn.tooltip = old;
      return this;
    };
  })($);

  return $;
})();
